#include "HLServerInfo.h"
#include <iostream>
#include <stdexcept>

#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>

#include <config.h>
#include <compat.h>

HLServerInfo::~HLServerInfo()
{
	this->closeConnection();
}

HLServerInfo::HLServerInfo(const char *_addr, int _port)
{
        char buf[BUFSIZ];
        struct hostent he;
        int herrno;
        struct hostent *result = NULL;

        sock = -1;
        challenge = 0;

        memset(&addr, 0, sizeof(struct sockaddr_in));
        this->addr.sin_family = AF_INET;
        this->addr.sin_port = htons(_port);

        if(gethostbyname_r(_addr, &he, buf, sizeof(buf), &result, &herrno) == 0) {
                memcpy(&this->addr.sin_addr.s_addr, he.h_addr_list[0], he.h_length);
        } else {
                throw invalid_argument("Unabe to resolve hostname");
        }
}

HLServerInfo::HLServerInfo(struct sockaddr_in *_addr)
{
        memcpy(&addr, &_addr, sizeof(struct sockaddr_in));
        sock = -1;
        challenge = 0;
}

HLServerInfo::HLServerInfo(HLServerInfo &arg)
{
	memcpy(&addr, arg.getAddress(), sizeof(struct sockaddr_in));
}

bool HLServerInfo::update(int timeout_sec, int timeout_usec)
{
	char squery[] = {
		0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x45, 0x6E, 0x67, 0x69,
		0x6E, 0x65, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x00
	};
	char buf[3 * BUFSIZ] = {0xff, 0xff, 0xff, 0xff};
	char values[64];
	char *tmp;
	int r;
	//uint32_t challenge = this->getChallenge(timeout_sec, timeout_usec);
	//if(!challenge)
	//	return false;
	int sock = this->openConnection();
	string str;
	bool isMod;

	if((r = write(sock, squery, sizeof(squery))) <0) {
		cerr << __func__ << ": " << strerror(errno) << endl;
		return false;
	}

	if((r = this->readMultiReply(sock, buf, sizeof(buf), timeout_sec, timeout_usec))>0) {
		tmp = buf + 4;
		if(*tmp != 0x6d) {
		//if(*tmp != 'm') {
			cerr << __func__ << ": got wrong packet type " << (0xff & *tmp) << endl;
			return false;
		}
		tmp++;
		attributes["address"] = tmp;
		tmp += strlen(tmp);
		tmp++;
		attributes["name"] = tmp;
		tmp += strlen(tmp);
		tmp++;
		attributes["map"] = tmp;
		tmp += strlen(tmp);
		tmp++;
		attributes["gametype"] = tmp;
		tmp += strlen(tmp);
		tmp++;
		attributes["gamedescription"] = tmp;
		tmp += strlen(tmp);
		tmp++;

		snprintf(values, sizeof(values), "%u", 0xff & *tmp);
		str = values;
		attributes["numplayers"] = str;
		tmp ++;
		snprintf(values, sizeof(values), "%u", 0xff & *tmp);
		attributes["maxplayers"] = values;
		tmp ++;
		snprintf(values, sizeof(values), "%u", 0xff & *tmp);
		attributes["version"] = values;
		tmp ++;
		switch(*tmp) {
			case 'l':
				attributes["dedicated"] = "0";
				break;
			case 'd':
				attributes["dedicated"] = "1";
				break;
			case 'p':
				attributes["dedicated"] = "0";
				attributes["proxy"] = "1";
				break;
		}
		tmp ++;
		attributes["os"] = *tmp == 'w' ? "windows" : "linux";
		tmp ++;
		snprintf(values, sizeof(values), "%u", 0xff & *tmp);
		attributes["password"] = values;
		tmp ++;
		isMod = 0xff & *tmp;
		snprintf(values, sizeof(values), "%u", 0xff & *tmp);
		attributes["isMod"] = values;
		tmp ++;
		if(isMod) {
			int32_t *l;
			attributes["modurl"] = tmp;
			tmp += strlen(tmp);
			tmp++;
			attributes["moddlurl"] = tmp;
			tmp += strlen(tmp);
			tmp++;
			tmp++; // a single null byte here

			l = (int32_t *)tmp;
			snprintf(values, sizeof(values), "%i", *l);
			attributes["modversion"] = values;
			tmp+=sizeof(int32_t);
			l = (int32_t *)tmp;
			snprintf(values, sizeof(values), "%i", *l);
			attributes["modsize"] = values;
			tmp+=sizeof(int32_t);
			snprintf(values, sizeof(values), "%u", 0xff & *tmp);
			attributes["sv_only"] = values;
			tmp ++;
			snprintf(values, sizeof(values), "%u", 0xff & *tmp);
			attributes["cl_dll"] = values;
			tmp ++;
		}


		snprintf(values, sizeof(values), "%u", 0xff & *tmp);
		attributes["secure"] = values;
		tmp ++;
		snprintf(values, sizeof(values), "%u", 0xff & *tmp);
		attributes["bots"] = values;
		tmp++;


		return true;
	}
	if(r<0)
		cerr << __func__ << ": select: " << strerror(errno) << endl;
	return false;
}

